Skip to content

Fix a bug where the trailers of a streaming response was dropped by RetryingClient - #6213

Merged
ikhoon merged 3 commits into
line:mainfrom
ikhoon:split-unsplit
May 27, 2025
Merged

Fix a bug where the trailers of a streaming response was dropped by RetryingClient#6213
ikhoon merged 3 commits into
line:mainfrom
ikhoon:split-unsplit

Conversation

@ikhoon

@ikhoon ikhoon commented Apr 21, 2025

Copy link
Copy Markdown
Contributor

Motivation:

During the implementation of #5714, HttpResponse.split() was used to extract the headers from a streaming response. After applying a RetryRule, the new response was reconstructed in

response0 = HttpResponse.of(headers, splitResponse.body());

However, because SplitHttpResponse.body() only publishes the data payloads and handles trailers separately, the trailers was missing in the reconstructed response, leading to a bug.

Modifications:

  • Fixed SurroundingPublisher to allow the last element to be emitted asynchronously.
  • Added SplitHttpRequest.unsplit() and SplitHttpResponse.unsplit() to re-create an HttpResponse from the split headers, body and trailers.
  • Fixed RetryingClient to use .unsplit() for deliveriong trailers correctly.

Result:

Fix a regression where RetryingClient dropped trailers of streaming responses (since 1.32.4)

…etryingClient

Motivation:

During the implementation of line#5714, `HttpResponse.split()` was used to
extract the headers from a streaming response. After applying a `RetryRule`,
the new response was reconstructed in
https://github.com/line/armeria/blob/3a35abe68c80c0af8594600ce677df49e09a9e0e/core/src/main/java/com/linecorp/armeria/client/retry/RetryingClient.java#L429
However, because `SplitHttpResponse.body()` only publishes the data
payloads and handles trailers separately, the trailers was missing in
the reconstructed response, leading to a bug.

Modifications:

- Fixed `SurroundingPublisher` to allow emitting the last element
  asynchronously.
- Added `SplitHttpRequest.unsplit()` and `SplitHttpResponse.unsplit()`
  to re-create an `HttpResponse` from the split headers, body and
  trailers.
- Fixed `RetryingClient` to use `.unsplit()` for deliveriong trailers
  correctly.

Result:

Fix a regression where RetryingClient dropped trailers of streaming responses (since 1.32.4)
@ikhoon ikhoon added the defect label Apr 21, 2025
@ikhoon ikhoon added this to the 1.33.0 milestone Apr 21, 2025
@ikhoon
ikhoon marked this pull request as draft April 21, 2025 14:55
@codecov

codecov Bot commented Apr 21, 2025

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 87.50000% with 7 lines in your changes missing coverage. Please review.

Project coverage is 74.61%. Comparing base (8150425) to head (f250c29).
Report is 61 commits behind head on main.

Files with missing lines Patch % Lines
...java/com/linecorp/armeria/common/HttpResponse.java 80.00% 2 Missing ⚠️
...com/linecorp/armeria/common/SplitHttpResponse.java 77.77% 1 Missing and 1 partial ⚠️
...a/internal/common/stream/SurroundingPublisher.java 91.30% 1 Missing and 1 partial ⚠️
...orp/armeria/common/PublisherBasedHttpResponse.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6213      +/-   ##
============================================
+ Coverage     74.46%   74.61%   +0.14%     
- Complexity    22234    22484     +250     
============================================
  Files          1963     1974      +11     
  Lines         82437    83034     +597     
  Branches      10764    10802      +38     
============================================
+ Hits          61385    61952     +567     
- Misses        15918    15935      +17     
- Partials       5134     5147      +13     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ikhoon
ikhoon marked this pull request as ready for review April 22, 2025 02:36
}
}

private void handleTail(@Nullable T tail, @Nullable Throwable cause) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the cause is always null because tailFuture never completes exceptionally.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tailFuture can be specified by a user.

static HttpResponse of(ResponseHeaders headers,
Publisher<? extends HttpData> publisher,
CompletableFuture<HttpHeaders> trailers) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I missed that. 😓

@minwoox minwoox left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


SurroundingSubscriber(@Nullable T head, StreamMessage<T> publisher,
Function<@Nullable Throwable, ? extends @Nullable T> finalizer,
Function<@Nullable Throwable, ? extends @Nullable CompletableFuture<T>> finalizer,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood that the throwable can be null, the CF can be null, and the tail handeld by the CF can also be null.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. All can be null.

@github-actions github-actions Bot added the Stale label May 23, 2025
@ikhoon
ikhoon merged commit 1051e96 into line:main May 27, 2025
@ikhoon ikhoon removed the Stale label May 27, 2025
@ikhoon
ikhoon deleted the split-unsplit branch May 27, 2025 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants